using System;
using System.Text;
using System.IO;

using iTextSharp.text;
using iTextSharp.text.pdf;

namespace GeneratorZostav
{
    /// <summary>
    /// Vygeneruje PDF dokument zo zadaneho vstupneho textoveho suboru.
    /// Rozdeli stream znakov na riadky dlhe definovany pocet znakov plus 
    /// na jednej strane bude len definovany pocet riadkov.
    /// Tieto udaje definuju prve dva riadky suboru.
    /// </summary>
    class CGeneratorZostavy
    {
        /// <summary>
        /// stream vstupneho suboru
        /// </summary>
        private FileStream m_fsIn;

        /// <summary>
        /// vstupny stream
        /// </summary>
        private StreamReader m_srIn;

        /// <summary>
        /// Vygenerovany PDF dokument
        /// </summary>
        private Document m_PdfDoc;

        /// <summary>
        /// Pocet znakov na riadok
        /// </summary>
        private int m_iPocZnakov;

        /// <summary>
        /// Pocet riadkov na stranu
        /// </summary>
        private int m_iPocRiadkov;

        /// <summary>
        /// Orientaciu dokumentu. True ak na lezato. False ak na stojato
        /// </summary>
        private bool m_bOrientaciaPDF;

        /// <summary>
        /// velkost fontu 
        /// </summary>
        private int m_iFontVelkost = 11;
        private int m_iFontVelkostLezato = 8;

        /// <summary>
        /// Aktualne zapisany pocet riakov pri generovani stranky
        /// </summary>
        private int m_iCurPocRiadkov = 0;

		/// <summary>
		/// Je povolene farbenie v generovanom dokumente?
		/// </summary>
		private bool m_EnabledColors = false;
		/// <summary>
		/// Kolko riadkov z hora ignorovat pri farbeni.
		/// </summary>
		private int m_NonColoredRowsCount = 0;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="sFileNameIn">Meno vstupneho suboru</param>
        /// <param name="sFilenameOut">Meno vystupneho PDF suboru.</param>
        public CGeneratorZostavy(string sFileNameIn, string sFileNameOut)
        {
            this.Open(sFileNameIn, sFileNameOut);
        }

		/// <summary>
		/// Treti riadok vstupu obsahuje informacie o farbeni dokumentu.
		/// </summary>
		/// <remarks>
		/// Ak je prvy znak "1", potom je farbenie povolene. Inak nie je.
		/// Treti a dalsie znaky obsahuju cislo, ktore hovori o pocte ignorovanych
		/// riadkov pri farbenie zhora.
		/// </remarks>
		/// <param name="line"></param>
		protected void SetColorInfo(string line)
		{
			if (string.IsNullOrEmpty(line))
			{
				throw new Exception("Riadok s informaciami o farbeni je prazdny.");
			}

			try
			{
				if (line[0] == '1')
				{
					this.m_EnabledColors = true;
				}

				if (line.Length > 2)
				{
					this.m_NonColoredRowsCount = int.Parse(line.Substring(2));
				}
			}
			catch (Exception ex)
			{
				throw new Exception("Nepodarilo sa nacitat data o farbeni z tretieho riadku vstupu.", ex);
			}
		}

        /// <summary>
        /// Inicializuje IO streamy a PDF generator.
        /// </summary>
        /// <param name="sFileNameIn"></param>
        /// <param name="sFileNameOut"></param>
        public void Open(string sFileNameIn, string sFileNameOut)
        {
            // pre istotu zatvorime, ak bolo nieco otvorene.
            this.Close();
            
            try
            {
                // otvorenie vstupneho suboru na zapis
                m_fsIn = new FileStream(sFileNameIn, FileMode.Open, FileAccess.Read, FileShare.Read);
                m_srIn = new StreamReader(m_fsIn);

                // nacitanie poctu znakov na riadok a riadkov na stranu a orientaciu dokumentu
                string s;

                s = this.m_srIn.ReadLine();
                if (Int32.TryParse(s, out this.m_iPocZnakov) == false)
                    throw new Exception("Nepodarilo sa ziskat pocet znakov na riadok zo vstupneho suboru.");

                s = this.m_srIn.ReadLine();
                if (Int32.TryParse(s, out this.m_iPocRiadkov) == false)
                    throw new Exception("Nepodarilo sa ziskat pocet riadkov na stranu zo vstupneho suboru.");

                s = this.m_srIn.ReadLine();
                int iOrientaciaDokumentu;
                if (Int32.TryParse(s, out iOrientaciaDokumentu) == false)
                    throw new Exception("Nepodarilo sa ziskat orientaciu vysledneho PDF dokumentu.");
                m_bOrientaciaPDF = (iOrientaciaDokumentu == 0) ? false : true;

				// farbenie
				s = this.m_srIn.ReadLine();
				this.SetColorInfo(s);


                // Vytvorenie PDF dokumentu
                Rectangle oVelkostStrany;
                if (m_bOrientaciaPDF == false)
                    oVelkostStrany = PageSize.A4;
                else
                    oVelkostStrany = PageSize.A4.Rotate();

                this.m_PdfDoc = new Document(oVelkostStrany, 36, 36, 36, 36);

                // prepojenie dokumentu a file streamu
                PdfWriter writer = PdfWriter.GetInstance(this.m_PdfDoc, new FileStream(sFileNameOut, FileMode.Create));

                // nastavenie prehliadaca
                writer.ViewerPreferences = PdfWriter.PageLayoutOneColumn | PdfWriter.FitWindow |
                                           PdfWriter.CenterWindow | PdfWriter.PageModeUseThumbs;

                // pridanie hlaviciek
                m_PdfDoc.AddTitle("Zostava");
                m_PdfDoc.AddAuthor("");
                m_PdfDoc.AddSubject("Zostava na tlacenie.");
                m_PdfDoc.AddKeywords("zostava");
                m_PdfDoc.AddCreator("GeneratorZostav 1.4. By Slavomir Hudak 2007-2009.");
            }

            catch (DocumentException ex)
            {
                throw new Exception("Chyba pri vytvarani PDF dokumentu.", ex);
            }

            catch (IOException ex)
            {
                throw new Exception("Input/Output chyba! Pravdepodobne sa neda nacitat vstupny subor \""+ sFileNameIn +"\" alebo vytvorit vystupny subor \"" + sFileNameOut + "\".", ex);
            }

            catch (Exception ex)
            {
                throw new Exception("Chyba pri otvarani vstupneho suboru \"" + sFileNameIn + "\", resp. vytvarani vystupneho suboru \"" + sFileNameOut + "\".", ex);
            }
        }

        /// <summary>
        /// Zatvori vsetky pouzite IO streamy.
        /// <para>Mala by sa zavolat po vygenerovani pdf dokumentu.</para>
        /// </summary>
        public void Close()
        {
            if (this.m_PdfDoc!=null) this.m_PdfDoc.Close();
            if (this.m_srIn!=null) this.m_srIn.Close();
            if (this.m_srIn!=null) this.m_fsIn.Close();
        }

		/// <summary>
		/// Ofarbi riadok sedou farbou ak treba.
		/// </summary>
		protected void OfarbiRiadok(Paragraph riadok)
		{
			if (this.m_iCurPocRiadkov % 2 == 0
				&& this.m_iCurPocRiadkov >= this.m_NonColoredRowsCount
				&& this.m_EnabledColors
				)
			{
				Chunk c = riadok.Chunks[0] as Chunk;
				c.SetBackground(Color.LIGHT_GRAY);
			}
		}

        /// <summary>
        /// Rozdeli riadok na definovany pocet znakov a zapise riadok do otvoreneho pdf streamu
        /// </summary>
        /// <param name="riadok">Text na spracovanie a zapis.</param>
        /// <returns>Zapise sa iba definovany pocet znakov. Zvysok sa vrati na dalsie spracovanie.</returns>
        private string OrezRiadok(string sRiadok)
        {
            string sCurRiadok = ""; // po znaku pridavane zo vstupneho riadka, ak je dostatocne velky tak newline
            int iCharCount = 0; // index na prave spracuvavany znak zo vstupneho stringu
            Paragraph oRiadok; // riadok pridany do PDF dokumentu

            while (iCharCount < sRiadok.Length)
            {
                sCurRiadok += sRiadok[iCharCount++];

                // Nacitali sme dostatocny pocet znakov na riadok
                // Zapiseme ich do editora a nacitavame znova
                if (sCurRiadok.Length == this.m_iPocZnakov)
                {
                    // ak sme uz zapisali dost riadkov na stranu, vygeneruj novu stranu
                    if (this.m_iCurPocRiadkov >= this.m_iPocRiadkov)
                    {
                        this.m_iCurPocRiadkov = 0;
                        this.m_PdfDoc.NewPage();
                    }

                    int iSize = (this.m_bOrientaciaPDF == true) ? m_iFontVelkostLezato : m_iFontVelkost;
                    oRiadok = new Paragraph(12, sCurRiadok,
                                            FontFactory.GetFont(FontFactory.COURIER, iSize, Font.NORMAL));

					this.OfarbiRiadok(oRiadok);


                    this.m_PdfDoc.Add(oRiadok);
                    sCurRiadok = "";
                    
                    // zapamatame si, kolko riadkov sme uz zapisali
                    this.m_iCurPocRiadkov++;
                }
            }

            // vratime nacitane ale nezapisane znaky
            return sCurRiadok;
        }

        /// <summary>
        /// Vygeneruje PDF dokument zostavy na tlacenie zo vstupneho suboru
        /// </summary>
        public void Generuj()
        {
            try
            {
                // inicializacia PDF dokumentu
                this.m_PdfDoc.Open();

                // Nacitavanie vstupneho suboru

                // nacitany riadok z dat
                string sRiadok;
                // sufix nacitaneho riadka, ktory sme kvoli nedostatocnej velkosti nemohli zapisat
                string sOffsetOrezaneho = "";

                // nacitaj prvy riadok, spracuj, zapis do editora a rekurzivne spracuj dalsie riadky
                while ((sRiadok = this.m_srIn.ReadLine()) != null)
                {
                    sOffsetOrezaneho = OrezRiadok(sOffsetOrezaneho + sRiadok);
                }

                // ak ostalo nieco nezapisane, tak dopiseme
                if (sOffsetOrezaneho.Length > 0)
                {
                    int iSize = (this.m_bOrientaciaPDF == true) ? m_iFontVelkostLezato : m_iFontVelkost;
                    Paragraph oRiadok = new Paragraph(sOffsetOrezaneho,
                                                      FontFactory.GetFont(FontFactory.COURIER, iSize, Font.NORMAL));
					this.OfarbiRiadok(oRiadok);
					this.m_PdfDoc.Add(oRiadok);
                }
            }

            catch (Exception ex)
            {
                throw new Exception("Nepodarilo sa vygenerovat PDF subor", ex);
            }

            finally
            {
                // zatvorime vsetky streamy
                this.Close();
            }
        }
    }
}
